﻿using System;
using System.Diagnostics;
using System.IO;
using ConsoleDump;

namespace FP.Automation.Utilities
{
	public class SLogger
	{
		
		#region Constructors

		protected SLogger()
		{
			var appInfo = new FileInfo(System.Reflection.Assembly.GetEntryAssembly().Location);
			var appName = appInfo.Name.Replace(appInfo.Extension,".log");
			var logFullname = string.Format(
				"{4}\\{0}-{1}-{2}_{3}",
				DateTime.Now.Year,
				DateTime.Now.Month,
				DateTime.Now.Day,
				appName,
				appInfo.DirectoryName
			);

			this._LogFileInfo = new FileInfo(logFullname);
		}

		#endregion

		#region Static Properties
		
		/// <summary>
		/// Get the stored SLogger object instance.
		/// Controls access and creation of the 
		/// SLogger object instance. SLogger is
		/// Singleton Logger.
		/// </summary>
		public static SLogger Instance
		{
			get
			{
				if (instance == null)
				{
					instance = new SLogger();
				}
				return instance;
			}
		}
		private static SLogger instance = null;

		#endregion

		#region Instance Properties

		private FileInfo _LogFileInfo = null;
		public FileInfo LogFileInfo
		{
			get
			{
				return this._LogFileInfo;
			}
		}

		public bool ShouldWriteToConsole 
		{ 
			get
			{
				#if (DEBUG)
					return true;
				#elif (!DEBUG)
					return false;
				#endif
			}
		}
		
		#endregion

		#region Logging

		/// <summary>
		/// Responsible for formatting the msg for the log and writing to the stream
		/// </summary>
		/// <param name="msg">the msg that was formatted for the console</param>
		/// <returns>returns false if an exception is caught</returns>
		private bool WriteToLog(string msg)
		{
			try
			{
				StreamWriter logStream = null;
				var now = DateTime.Now;
				var msgText = string.Format("{0} {1} {2}",now.ToShortDateString(), now.ToShortTimeString(), msg);
				if(msg.StartsWith("[DEBUG]") || ShouldWriteToConsole)
				{
					using(logStream = new StreamWriter(this.LogFileInfo.FullName,true))
					{
						logStream.WriteLine(msgText);
					}
				}
				return true;
			}
			catch
			{
				return false;
			}
	}
		
		/// <summary>
		/// responsible for formatting and writting the log msg to the console
		/// </summary>
		/// <param name="msg">log message</param>
		/// <param name="context">the name of method logging the message</param>
		/// <param name="level">the level of logging INFO, WARN, etc</param>
		/// <param name="foregroundColor">the color to use when writting the message to the console</param>
		/// <returns>returns false if exception is caught</returns>
		private string WriteToConsole(string msg, string context, LoggingLevel level, ConsoleColor foregroundColor)
		{
			try
			{
				//format the message for console and then passed to log
				var msgText = string.Format("[{0}]\t<{1}> - {2}", level, context, msg);

				//get starting fore color and set it to the specified color
				var startFore = Console.ForegroundColor;
				Console.ForegroundColor = foregroundColor;

				if (level != LoggingLevel.DEBUG || ShouldWriteToConsole)
				{ 
					Console.WriteLine(msgText);
				}

				//restore console fore color
				Console.ForegroundColor = startFore;

				return msgText;

			}
			catch
			{
				return msg;
			}

		}
		
		#endregion

		#region Infos
		
		public void Info(string msg)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.INFO, ConsoleColor.White);
				this.WriteToLog(msgText);
			}
			catch { }
		}
			
		public void Info(string formatMsg, params object[] args)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msg = string.Format(formatMsg, args);
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.INFO, ConsoleColor.White);
				this.WriteToLog(msgText);
			}
			catch { }
		}

		#endregion

		#region Debugs

		public void Debug(string msg)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.INFO, ConsoleColor.DarkGreen);
				this.WriteToLog(msgText);
			}
			catch { }
		}

		public void Debug(string formatMsg, params object[] args)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msg = string.Format(formatMsg, args);
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.INFO, ConsoleColor.DarkGreen);
				this.WriteToLog(msgText);
			}
			catch { }
		}

		#endregion

		#region Warns

		public void Warn(string msg)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.WARN, ConsoleColor.Yellow);
				this.WriteToLog(msgText);
			}
			catch { }
		}

		public void Warn(string formatMsg, params object[] args)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msg = string.Format(formatMsg, args);
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.WARN, ConsoleColor.Yellow);
				this.WriteToLog(msgText);
			}
			catch { }
		}

		#endregion
		
		#region Errors

		public void Error(string msg)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.ERROR, ConsoleColor.Red);
				this.WriteToLog(msgText);
			}
			catch { }
		}

		public void Error(string formatMsg, params object[] args)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msg = string.Format(formatMsg, args);
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.ERROR, ConsoleColor.Red);
				this.WriteToLog(msgText);
			}
			catch { }
		}

		#endregion

		#region Fatals

		public void Fatal(string msg)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.FATAL, ConsoleColor.DarkRed);
				this.WriteToLog(msgText);
				throw new Exception(msg);
			}
			catch (Exception ex) 
			{ 
				throw ex;
			}
		}

		public void Fatal(string formatMsg, params object[] args)
		{
			try
			{
				var context = this.GetCallersFullName(new StackTrace());
				var msg = string.Format(formatMsg, args);
				var msgText = this.WriteToConsole(msg, context, LoggingLevel.FATAL, ConsoleColor.DarkRed);
				this.WriteToLog(msgText);
				throw new Exception(msg);
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		#endregion

		#region Misc

		private string GetCallersFullName(StackTrace trace)
		{
			var methodInfo = trace.GetFrame(1).GetMethod();
			return string.Format("{0}.{1}", methodInfo.ReflectedType.FullName, methodInfo.Name);
		}

		public void DumpToConsole(Object inputObject, string dumpLabel="Object Dump")
		{
			inputObject.Dump(dumpLabel);
		}

		#endregion
	}
}
